#include "TCPSocket.hpp"

#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#include <cstring>
#include <iostream>

TCPSocket::TCPSocket() : sockfd_(-1) {
    sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd_ < 0) {
        std::cerr << "socket creation failed\n";
        throw std::runtime_error("socket failed");
    }
}

TCPSocket::~TCPSocket() {
    if (sockfd_ >= 0) ::close(sockfd_);
}

bool TCPSocket::bindAndListen(const std::string& ip, uint16_t port, int backlog) {
    sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = ip.empty() ? INADDR_ANY : inet_addr(ip.c_str());

    int opt = 1;
    setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    if (bind(sockfd_, (sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind failed");
        return false;
    }
    if (listen(sockfd_, backlog) < 0) {
        perror("listen failed");
        return false;
    }
    return true;
}

int TCPSocket::accept() {
    sockaddr_in cliAddr{};
    socklen_t len = sizeof(cliAddr);
    int clientFd = ::accept(sockfd_, (sockaddr*)&cliAddr, &len);
    if (clientFd < 0) {
        perror("accept failed");
    }
    return clientFd;
}

bool TCPSocket::connect(const std::string& ip, uint16_t port) {
    sockaddr_in servAddr{};
    servAddr.sin_family = AF_INET;
    servAddr.sin_port = htons(port);
    if (inet_pton(AF_INET, ip.c_str(), &servAddr.sin_addr) <= 0) {
        perror("inet_pton failed");
        return false;
    }
    if (::connect(sockfd_, (sockaddr*)&servAddr, sizeof(servAddr)) < 0) {
        perror("connect failed");
        return false;
    }
    return true;
}

void TCPSocket::close() {
    if (sockfd_ >= 0) {
        ::close(sockfd_);
        sockfd_ = -1;
    }
}

int TCPSocket::getFd() const {
    return sockfd_;
}
